%Options escape=$,la=3,pros=MIN,nogoto-default,em,scopes
%Options table=java,package=lpg,fp=Lpg,prefix=TK_
%Options action-block=("*act.java", "/.", "./")

%Define ----------------------------------------------------------------

    $Header
    /.
        //
        // Rule $rule_number: $rule_text
        //./

    $BeginJava
    /.$Header
        case $rule_number:
        {   // Line $next_line "$input_file$"./

    $EndJava
    /.}
        break;
./

    $NoAction
    /.$Header
        case $rule_number: break;./

    $BeginActions
    /.
    void ruleAction(int ruleNumber)
    {
        switch(ruleNumber)
        {./

    $EndActions
    /.
        default:
            assert false;
        }
    }./

%End

%Terminals

    DROPSYMBOLS_KEY             ::= "%DropSymbols"
    DROPACTIONS_KEY             ::= "%DropActions"
    DROPRULES_KEY               ::= "%DropRules"
    NOTICE_KEY                  ::= "%Notice"
    AST_KEY                     ::= "%Ast"
    GLOBALS_KEY                 ::= "%Globals"
    DEFINE_KEY                  ::= "%Define"
    TERMINALS_KEY               ::= "%Terminals"
    SOFTKEYWORDS_KEY            ::= "%SoftKeywords"
    EOL_KEY                     ::= "%Eol"
    EOF_KEY                     ::= "%Eof"
    ERROR_KEY                   ::= "%Error"
    IDENTIFIER_KEY              ::= "%Identifier"
    ALIAS_KEY                   ::= "%Alias"
    EMPTY_KEY                   ::= "%Empty"
    START_KEY                   ::= "%Start"
    TYPES_KEY                   ::= "%Types"
    RULES_KEY                   ::= "%Rules"
    NAMES_KEY                   ::= "%Names"
    END_KEY                     ::= "%End"
    HEADERS_KEY                 ::= "%Headers"
    TRAILERS_KEY                ::= "%Trailers"
    EXPORT_KEY                  ::= "%Export"
    IMPORT_KEY                  ::= "%Import"
    INCLUDE_KEY                 ::= "%Include"
    PROSTHESES_KEY              ::= "%Prostheses"
    RECOVER_KEY                 ::= "%Recover"
    DISJOINTPREDECESSORSETS_KEY ::= "%DisjointPredecessorSets"
    EQUIVALENCE                 ::= "::="
    PRIORITY_EQUIVALENCE        ::= "::=?"
    ARROW                       ::= "->"
    PRIORITY_ARROW              ::= "->?"
    OR_MARKER                   ::= "|"

    MACRO_NAME
    SYMBOL
    BLOCK

%End

%Eof
   EOF
%End

%Error
    ERROR_SYMBOL
%End

%Start

    LPG_INPUT

%End

%Headers

/.import java.util.List;

class Lpgact
{$BeginActions./

%End

%Rules

    LPG_INPUT ::= Grammar
        /.$BeginJava
            //
            // If start symbols are specified in this source file, then
            // they override all imported start symbols, if any.
            //
            if (startIndexes.isEmpty())
            {
                //
                // If no start symbol is specified in this source file but this source
                // file contains more than one imported file with one or more start symbols
                // specified in them then it's an error.
                // If one and only one of the imported files contain start symbol
                // specification(s) then we inherit these start symbols.
                //
                int importedSize = importedStartIndexes.size();
                if (importedSize > 1)
                {
                    for (int i = 0; i < importedSize; i++)
                         option.emitError(importedStartIndexes.get(i).importFileIndex,
                                             "Conflicting start symbol(s) specified in this imported file");
                    control.exit();
                }
                else if (importedSize == 1)
                {
                    ImportedStartIndexes element = importedStartIndexes.get(0);
                    startIndexes.addAll(element.startIndexes);
                }
            }

            importedStartIndexes.clear(); // free up space.
        $EndJava./

    Grammar ::= %Empty
        /.$NoAction./

    Grammar ::= Grammar include_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar notice_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar define_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar terminals_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar export_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar import_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar softkeywords_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar eof_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar eol_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar error_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar prostheses_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar recover_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar identifier_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar start_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar alias_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar names_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar headers_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar ast_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar globals_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar trailers_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar rules_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar types_segment END_KEY_opt
        /.$NoAction./

    Grammar ::= Grammar dps_segment END_KEY_opt
        /.$NoAction./

    include_segment ::= INCLUDE_KEY
        /.$NoAction./

    include_segment ::= INCLUDE_KEY SYMBOL
        /.$NoAction./

    notice_segment ::= NOTICE_KEY
        /.$NoAction./

    notice_segment ::= notice_segment action_segment
        /.$BeginJava
            noticeBlocks.add(getToken(2));
        $EndJava./

    define_segment ::= DEFINE_KEY
        /.$NoAction./

    define_segment ::= define_segment macro_name_symbol macro_segment
        /.$BeginJava
            MacroSymbol macroSymbol = lexStream.getMacroSymbol(getToken(2));
            assert (macroSymbol != null);
            macroSymbol.setBlock(getToken(3));
        $EndJava./

    macro_name_symbol ::= MACRO_NAME
        /.$NoAction./

    macro_name_symbol ::= SYMBOL -- warning: escape prefix missing...
        /.$BeginJava
            int token1 = getToken(1);
            String macroName = option.escape + lexStream.nameString(token1);
            MacroSymbol macroSymbol = macroTable.find(macroName);
            if (macroSymbol == null)
            {
                macroSymbol = new MacroSymbol(macroName, macroTable.size());
                macroTable.insert(macroName, macroSymbol);
                option.emitWarning(token1, Parser.ActError.MACRO_EXPECTED_INSTEAD_OF_SYMBOL.msg);
            }

            lexStream.getTokenReference(token1).setSymbol(macroSymbol);
        $EndJava./

    macro_segment ::= BLOCK
        /.$NoAction./

    terminals_segment ::= TERMINALS_KEY
        /.$NoAction./

    terminals_segment ::= terminals_segment terminal_symbol
        /.$BeginJava
            terminals.add(getToken(2));
        $EndJava./

    terminals_segment ::= terminals_segment terminal_symbol produces name
        /.$BeginJava
            int token2 = getToken(2);
            int token4 = getToken(4);
            terminals.add(token2);
            names.add(new NameDefinition(token2, token4));
            aliases.add(new AliasDefinition(token4, token2));
        $EndJava./

    export_segment ::= EXPORT_KEY
        /.$NoAction./

    export_segment ::= export_segment terminal_symbol
        /.$BeginJava
            exports.add(getToken(2));
        $EndJava./

    import_segment ::= IMPORT_KEY
        /.$NoAction./

    import_segment ::= IMPORT_KEY SYMBOL {drop_command}
        /.$BeginJava
            int currentIndex = lexStream.peek();
            int startIndex = lexStream.numTokens();
            int token2 = getToken(2);

            Scanner scanner = new Scanner(option, lexStream, variableTable, macroTable);
            scanner.scanFile(token2);

            if (scanner.numErrorTokens() > 0)
                control.exit();
            else // file found and scanned with no errors?
            {
                //
                // If the file is already in the process of being included, issue an error and stop.
                //
                InputFileSymbol importFile = lexStream.findOrInsertFile(
                        option.includeSearchDirectory, lexStream.nameString(token2));
                assert (importFile != null);

                if (importFile.isLocked())
                {
                    option.emitWarning(token2, Parser.ActError.RECURSIVE_IMPORT.msg);
                    control.exit();
                }
                else
                {
                    //
                    // Lock the include_file to avoid looping...
                    //
                    importFile.lock();

                    Parser parser = new Parser(control, lexStream, variableTable, macroTable);
                    parser.parse(startIndex);

                    this.parser.merge(token2, parser);

                    importFile.unlock();
                }
            }

            droppedRules.clear();
            lexStream.reset(currentIndex);
        $EndJava./

    drop_command ::= drop_symbols
        /.$NoAction./

    drop_command ::= drop_rules
        /.$NoAction./

    --
    -- TODO: NOT YET IMPLEMENTED !!!
    --
    drop_command ::= DROPACTIONS_KEY
        /.$NoAction./

    drop_symbols ::= DROPSYMBOLS_KEY
        /.$NoAction./

    drop_symbols ::= drop_symbols SYMBOL
        /.$BeginJava
            droppedRules.add(new RuleDefinition(getToken(2), 0, 0));
        $EndJava./

    drop_rules ::= DROPRULES_KEY
        /.$NoAction./

    drop_rules ::= drop_rules drop_rule
        /.$NoAction./

    drop_rule ::= SYMBOL produces rhs
        /.$BeginJava
            droppedRules.add(new RuleDefinition(getToken(1), getToken(2), parser.curTok));
        $EndJava./

    drop_rule ::= SYMBOL MACRO_NAME produces rhs
        /.$BeginJava
            droppedRules.add(new RuleDefinition(getToken(1), getToken(3), parser.curTok));
        $EndJava./

    drop_rule ::= drop_rule '|' rhs
        /.$BeginJava
            RuleDefinition rule = droppedRules.get(droppedRules.size() - 1);
            droppedRules.add(new RuleDefinition(rule.lhsIndex, getToken(2), parser.curTok));
        $EndJava./

    {drop_command} ::= %Empty
        /.$NoAction./

    {drop_command} ::= {drop_command} drop_command
        /.$NoAction./

    softkeywords_segment ::= SOFTKEYWORDS_KEY
        /.$NoAction./

    softkeywords_segment ::= softkeywords_segment terminal_symbol
        /.$BeginJava
            keywords.add(getToken(2));
        $EndJava./

    softkeywords_segment ::= softkeywords_segment terminal_symbol produces name
        /.$BeginJava
            int token2 = getToken(2);
            int token4 = getToken(4);
            keywords.add(token2);
            names.add(new NameDefinition(token2, token4));
            aliases.add(new AliasDefinition(token4, token2));
        $EndJava./

    error_segment ::= ERROR_KEY
        /.$NoAction./

    error_segment ::= ERROR_KEY terminal_symbol
        /.$BeginJava
            parser.setErrorIndex(getToken(2));
        $EndJava./

    prostheses_segment ::= PROSTHESES_KEY
        /.$NoAction./

    prostheses_segment ::= prostheses_segment prosthesis
        /.$NoAction./

    prosthesis ::= SYMBOL action_segment_opt
        /.$BeginJava
            // Set separatorIndex to curTok to facilitate (null) iteration over the rhs
            prostheses.add(new RuleDefinition(getToken(1), parser.curTok, parser.curTok));
        $EndJava./

    prosthesis ::= SYMBOL MACRO_NAME action_segment_opt
        /.$BeginJava
            // Set separatorIndex to curTok to facilitate (null) iteration over the rhs
            prostheses.add(new RuleDefinition(getToken(1), parser.curTok, parser.curTok, getToken(2), 0));
        $EndJava./

    prosthesis ::= SYMBOL MACRO_NAME MACRO_NAME action_segment_opt
        /.$BeginJava
            parser.addVariableName(getToken(3));

            // Set separatorIndex to curTok to facilitate (null) iteration over the rhs
            prostheses.add(new RuleDefinition(getToken(1), parser.curTok, parser.curTok, getToken(2), getToken(3)));
        $EndJava./

    action_segment_opt ::= %Empty
        /.$NoAction./

    action_segment_opt ::= action_segment
        /.$NoAction./

    recover_segment ::= RECOVER_KEY
        /.$NoAction./

    recover_segment ::= recover_segment terminal_symbol
        /.$BeginJava
            recovers.add(getToken(2));
        $EndJava./

    identifier_segment ::= IDENTIFIER_KEY
        /.$NoAction./

    identifier_segment ::= IDENTIFIER_KEY terminal_symbol
        /.$BeginJava
            parser.setIdentifierIndex(getToken(2));
        $EndJava./

    eol_segment ::= EOL_KEY
        /.$NoAction./

    eol_segment ::= EOL_KEY terminal_symbol
        /.$BeginJava
            parser.setEolIndex(getToken(2));
        $EndJava./

    eof_segment ::= EOF_KEY
        /.$NoAction./

    eof_segment ::= EOF_KEY terminal_symbol
        /.$BeginJava
            parser.setEofIndex(getToken(2));
        $EndJava./

    terminal_symbol ::= SYMBOL
        /.$NoAction./

    terminal_symbol ::= MACRO_NAME -- warning: escape prefix used in symbol
        /.$BeginJava
            parser.changeMacroToVariable(getToken(1));
        $EndJava./

    alias_segment ::= ALIAS_KEY
        /.$NoAction./

    alias_segment ::= alias_segment ERROR_KEY produces alias_rhs
        /.$BeginJava
            parser.setErrorIndex(getToken(4));
        $EndJava./

    alias_segment ::= alias_segment EOL_KEY produces alias_rhs
        /.$BeginJava
            parser.setEolIndex(getToken(4));
        $EndJava./

    alias_segment ::= alias_segment EOF_KEY produces alias_rhs
        /.$BeginJava
            parser.setEofIndex(getToken(4));
        $EndJava./

    alias_segment ::= alias_segment IDENTIFIER_KEY produces alias_rhs
        /.$BeginJava
            parser.setIdentifierIndex(getToken(4));
        $EndJava./

    alias_segment ::= alias_segment SYMBOL produces alias_rhs
        /.$BeginJava
            aliases.add(new AliasDefinition(getToken(2), getToken(4)));
        $EndJava./

    alias_segment ::= alias_segment alias_lhs_macro_name produces alias_rhs
        /.$BeginJava
            aliases.add(new AliasDefinition(getToken(2), getToken(4)));
        $EndJava./

    alias_lhs_macro_name ::= MACRO_NAME -- warning: escape prefix used in symbol
        /.$BeginJava
            parser.changeMacroToVariable(getToken(1));
        $EndJava./

    alias_rhs ::= SYMBOL
        /.$NoAction./

    alias_rhs ::= MACRO_NAME -- warning: escape prefix used in symbol
        /.$BeginJava
            parser.changeMacroToVariable(getToken(1));
        $EndJava./

    alias_rhs ::= ERROR_KEY
        /.$NoAction./

    alias_rhs ::= EOL_KEY
        /.$NoAction./

    alias_rhs ::= EOF_KEY
        /.$NoAction./

    alias_rhs ::= EMPTY_KEY
        /.$NoAction./

    alias_rhs ::= IDENTIFIER_KEY
        /.$NoAction./

    start_segment ::= START_KEY
        /.$NoAction./

    start_segment ::= start_segment start_symbol
        /.$BeginJava
            startIndexes.add(getToken(2));
        $EndJava./

    headers_segment ::= HEADERS_KEY
        /.$NoAction./

    headers_segment ::= headers_segment action_segment
        /.$BeginJava
            headerBlocks.add(getToken(2));
        $EndJava./

    ast_segment ::= AST_KEY
        /.$NoAction./

    ast_segment ::= ast_segment action_segment
        /.$BeginJava
            astBlocks.add(getToken(2));
        $EndJava./

    globals_segment ::= GLOBALS_KEY
        /.$NoAction./

    globals_segment ::= globals_segment action_segment
        /.$BeginJava
            globalBlocks.add(getToken(2));
        $EndJava./

    trailers_segment ::= TRAILERS_KEY
        /.$NoAction./

    trailers_segment ::= trailers_segment action_segment
        /.$BeginJava
            trailerBlocks.add(getToken(2));
        $EndJava./

    start_symbol ::= SYMBOL
        /.$NoAction./

    start_symbol ::= MACRO_NAME
        /.$NoAction./

    rules_segment ::= RULES_KEY {action_segment}
        /.$NoAction./

    rules_segment ::= rules_segment rules
        /.$NoAction./

    rules ::= SYMBOL produces rhs
        /.$BeginJava
            rules.add(new RuleDefinition(getToken(1), getToken(2), parser.curTok));
        $EndJava./

    rules ::= SYMBOL MACRO_NAME produces rhs
        /.$BeginJava
            rules.add(new RuleDefinition(getToken(1), getToken(3), parser.curTok, getToken(2), 0));
        $EndJava./

    rules ::= SYMBOL MACRO_NAME MACRO_NAME produces rhs
        /.$BeginJava
            parser.addVariableName(getToken(3));

            rules.add(new RuleDefinition(getToken(1), getToken(4), parser.curTok, getToken(2), getToken(3)));
        $EndJava./

    rules ::= rules '|' rhs
        /.$BeginJava
            RuleDefinition rule = rules.get(rules.size() - 1);
            rules.add(new RuleDefinition(rule.lhsIndex, getToken(2),
                    parser.curTok, rule.classnameIndex, rule.arrayElementTypeIndex));
        $EndJava./

    produces ::= '::='
        /.$NoAction./

    produces ::= '::=?'
        /.$NoAction./

    produces ::= '->'
        /.$NoAction./

    produces ::= '->?'
        /.$NoAction./

    rhs ::= %Empty
        /.$NoAction./

    rhs ::= rhs SYMBOL
        /.$NoAction./

    rhs ::= rhs SYMBOL MACRO_NAME
        /.$NoAction./

    rhs ::= rhs EMPTY_KEY
        /.$NoAction./

    rhs ::= rhs action_segment
        /.$NoAction./

    action_segment ::= BLOCK
        /.$NoAction./

    types_segment ::= TYPES_KEY
        /.$NoAction./

    types_segment ::= types_segment type_declarationlist
        /.$NoAction./

    type_declarationlist ::= type_declarations
        /.$NoAction./
                           | type_declarations BLOCK
        /.$BeginJava
            int index = types.size();
            do
            {
                types.get(--index).blockIndex = getToken(2);
            }
            while(lexStream.kind(types.get(index).separatorIndex) == Lpgsym.TK_OR_MARKER);
        $EndJava./

    type_declarations ::= SYMBOL produces SYMBOL
        /.$BeginJava
            types.add(new TypeDefinition(getToken(1), getToken(2), getToken(3), 0));
        $EndJava./
                        | type_declarations '|' SYMBOL
        /.$BeginJava
            TypeDefinition type = types.get(types.size() - 1);
            types.add(new TypeDefinition(type.typeIndex, getToken(2), getToken(3), 0));
        $EndJava./

    dps_segment ::= DISJOINTPREDECESSORSETS_KEY
        /.$NoAction./

    dps_segment ::= dps_segment SYMBOL SYMBOL
        /.$BeginJava
            predecessorCandidates.add(new PredecessorSetDefinition(getToken(2), getToken(3)));
        $EndJava./

    names_segment ::= NAMES_KEY
        /.$NoAction./

    names_segment ::= names_segment name produces name
        /.$BeginJava
            names.add(new NameDefinition(getToken(2), getToken(4)));
        $EndJava./

    name ::= SYMBOL
        /.$NoAction./

    name ::= MACRO_NAME -- warning: escape prefix used in symbol
        /.$BeginJava
            parser.changeMacroToVariable(getToken(1));
        $EndJava./

    name ::= EMPTY_KEY
        /.$BeginJava
            option.emitError(getToken(1), "Illegal use of empty name or empty keyword");
            control.exit();
        $EndJava./

    name ::= ERROR_KEY
        /.$NoAction./

    name ::= EOL_KEY
        /.$NoAction./

    name ::= IDENTIFIER_KEY
        /.$NoAction./

    END_KEY_opt ::= %Empty
        /.$NoAction./

    END_KEY_opt ::= END_KEY
        /.$NoAction./

    {action_segment} ::= %Empty
        /.$NoAction./

    {action_segment} ::= {action_segment} action_segment
        /.$BeginJava
            initialBlocks.add(getToken(2));
        $EndJava./

%End

%Trailers

/.$EndActions

    Lpgact(Parser parser)
    {
        this.parser                 = parser;
        this.control                = parser.control;
        this.option                 = parser.option;
        this.lexStream              = parser.lexStream;
        this.variableTable          = parser.variableTable;
        this.macroTable             = parser.macroTable;
        this.terminals              = parser.terminals;
        this.keywords               = parser.keywords;
        this.exports                = parser.exports;
        this.recovers               = parser.recovers;
        this.startIndexes           = parser.startIndexes;
        this.predecessorCandidates  = parser.predecessorCandidates;
        this.aliases                = parser.aliases;
        this.names                  = parser.names;
        this.noticeBlocks           = parser.noticeBlocks;
        this.globalBlocks           = parser.globalBlocks;
        this.astBlocks              = parser.astBlocks;
        this.headerBlocks           = parser.headerBlocks;
        this.initialBlocks          = parser.initialBlocks;
        this.trailerBlocks          = parser.trailerBlocks;
        this.rules                  = parser.rules;
        this.droppedRules           = parser.droppedRules;
        this.prostheses             = parser.prostheses;
        this.types                  = parser.types;
        this.importedStartIndexes   = parser.importedStartIndexes;
    }

    private int getToken(int i)
    {
        return parser.getToken(i);
    }

    private final Parser parser;
    private final Control control;
    private final Option option;
    private final LexStream lexStream;
    private final LookupTable<VariableSymbol> variableTable;
    private final LookupTable<MacroSymbol> macroTable;
    private final IntArrayList terminals;
    private final IntArrayList keywords;
    private final IntArrayList exports;
    private final IntArrayList recovers;
    private final IntArrayList startIndexes;
    private final List<PredecessorSetDefinition> predecessorCandidates;
    private final List<AliasDefinition> aliases;
    private final List<NameDefinition> names;
    private final IntArrayList noticeBlocks;
    private final IntArrayList globalBlocks;
    private final IntArrayList astBlocks;
    private final IntArrayList headerBlocks;
    private final IntArrayList initialBlocks;
    private final IntArrayList trailerBlocks;
    private final List<RuleDefinition> rules;
    private final List<RuleDefinition> droppedRules;
    private final List<RuleDefinition> prostheses;
    private final List<TypeDefinition> types;
    private final List<ImportedStartIndexes> importedStartIndexes;

}./

%End
